package com.yuke.cloud.service.uac.controller.frontend;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.yuke.cloud.common.security.auth.app.authentication.openid.OpenIdAuthenticationToken;
import com.yuke.cloud.common.util.wrapper.WrapMapper;
import com.yuke.cloud.common.util.wrapper.Wrapper;
import com.yuke.cloud.service.uac.dto.AuthUserDto;
import com.yuke.cloud.service.uac.entity.AccUser;
import com.yuke.cloud.service.uac.security.RedisClientDetailsService;
import com.yuke.cloud.service.uac.service.AccUserService;
import com.yuke.cloud.service.uac.service.AuthWxService;
import com.yuke.cloud.service.uac.utils.Result;
import com.yuke.cloud.service.uac.utils.SpringUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.MapUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.InternalAuthenticationServiceException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.common.exceptions.UnapprovedClientAuthenticationException;
import org.springframework.security.oauth2.provider.ClientDetails;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.OAuth2Request;
import org.springframework.security.oauth2.provider.TokenRequest;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.Writer;
import java.util.HashMap;
import java.util.Map;

import static com.yuke.cloud.common.util.WeChatUtil.decryptData;

/**
 * 登录授权
 *
 * @author zyq
 * @date 2021-03-6
 */
@Slf4j
@RestController
@RequestMapping("/auth/openid")
@Api(tags = "系统授权接口",value = "Web-AuthWxController", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public class AuthWxController {
    @Resource
    private ObjectMapper objectMapper;
    @Resource
    private PasswordEncoder passwordEncoder;
    @Resource
    private AuthWxService authWxService;
    @Resource
    private AccUserService accUserService;
    @Autowired
    private AuthenticationManager authenticationManager;
    @Resource
    private AuthorizationServerTokenServices authorizationServerTokenServices;

    @ApiOperation("微信小程序获取OPENID")
    @PostMapping(value = "/code2openid")
    public Wrapper<AuthUserDto> code2openid(@RequestBody AuthUserDto authUserDto, HttpServletRequest request) {
        return WrapMapper.ok(authWxService.code2openid(authUserDto));
    }

    @ApiOperation("微信小程序注册")
    @PostMapping(value = "/register")
    public Wrapper<AuthUserDto> wxRegister(@RequestBody AuthUserDto authUserDto, HttpServletRequest request) {
        return WrapMapper.ok(authWxService.wxRegist(authUserDto, request));
    }

    @ApiOperation("微信小程序登录授权")
    @PostMapping(value = "/login")
    public Wrapper<AuthUserDto> wxLogin(@RequestBody AuthUserDto authUserDto, HttpServletRequest request) {
        return WrapMapper.ok(authWxService.wxLogin(authUserDto, request));
    }

    @ApiOperation(value = "解密手机号", httpMethod = "POST", produces = "application/json;charset=UTF-8")
    @PostMapping("/getUserPhone")
    public Wrapper getUserPhone(@RequestBody AuthUserDto authUserDto){
        //(String sessionKey,String encryptedData,String iv)
        Map<String, Object> data = new HashMap<>();
        try {
            String phone = decryptData(authUserDto.getEncryptedData(), authUserDto.getSessionKey(), authUserDto.getIv());
            data.put("datas",phone);
        }catch (Exception e){
            e.printStackTrace();
            return WrapMapper.error("901,解密手机号失败！");
        }
        return WrapMapper.ok(data);
    }

    @ApiOperation(value = "openId获取token")
    @PostMapping("/oauth/openId/token")//
    public void getTokenByOpenId(
            @ApiParam(required = true, name = "openId", value = "openId") String openId,
            HttpServletRequest request, HttpServletResponse response) throws IOException {
        OpenIdAuthenticationToken token = new OpenIdAuthenticationToken(openId);
        AccUser member = accUserService.findByOpenId(openId);
        if (member!=null){
            writerToken(request, response, token, "openId错误",member.getUserId());
        }else {
            exceptionHandler(response, "openId错误");
        }

    }

    private void writerToken(HttpServletRequest request, HttpServletResponse response, AbstractAuthenticationToken token
            , String badCredenbtialsMsg,Long userId) throws IOException {
        try {
            String clientId = request.getHeader("client_id");
            String clientSecret = request.getHeader("client_secret");
            if (clientId == null || "".equals(clientId)) {
                throw new UnapprovedClientAuthenticationException("请求头中无client_id信息");
            }

            if (clientSecret == null || "".equals(clientSecret)) {
                throw new UnapprovedClientAuthenticationException("请求头中无client_secret信息");
            }

            ClientDetails clientDetails = getClient(clientId, clientSecret, null);
            TokenRequest tokenRequest = new TokenRequest(MapUtils.EMPTY_MAP, clientId, clientDetails.getScope(), "customer");
            OAuth2Request oAuth2Request = tokenRequest.createOAuth2Request(clientDetails);
            Authentication authentication = authenticationManager.authenticate(token);
            SecurityContextHolder.getContext().setAuthentication(authentication);
            OAuth2Authentication oAuth2Authentication = new OAuth2Authentication(oAuth2Request, authentication);
            OAuth2AccessToken oAuth2AccessToken =  authorizationServerTokenServices.createAccessToken(oAuth2Authentication);
            oAuth2Authentication.setAuthenticated(true);

            writerObj(response, oAuth2AccessToken);
        } catch (BadCredentialsException | InternalAuthenticationServiceException e) {
            exceptionHandler(response, badCredenbtialsMsg);
            e.printStackTrace();
        } catch (Exception e) {
            exceptionHandler(response, e);
        }
    }

    private void exceptionHandler(HttpServletResponse response, Exception e) throws IOException {
        log.error("exceptionHandler-error:", e);
        exceptionHandler(response, e.getMessage());
    }

    private void exceptionHandler(HttpServletResponse response, String msg) throws IOException {
        response.setStatus(HttpStatus.UNAUTHORIZED.value());
        writerObj(response, Result.failed(msg));
    }

    private void writerObj(HttpServletResponse response, Object obj) throws IOException {
        response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
        try (
                Writer writer = response.getWriter()
        ) {
            writer.write(objectMapper.writeValueAsString(obj));
            writer.flush();
        }
    }

    private ClientDetails getClient(String clientId, String clientSecret, RedisClientDetailsService clientDetailsService) {
        if (clientDetailsService == null) {
            clientDetailsService = SpringUtil.getBean(RedisClientDetailsService.class);
        }
        ClientDetails clientDetails = clientDetailsService.loadClientByClientId(clientId);

        if (clientDetails == null) {
            throw new UnapprovedClientAuthenticationException("clientId对应的信息不存在");
        } else if (!passwordEncoder.matches(clientSecret, clientDetails.getClientSecret())) {
            throw new UnapprovedClientAuthenticationException("clientSecret不匹配");
        }
        return clientDetails;
    }
}
